package com.access;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Stemmer {

	// data for inserting into db
	// word -> [a list of (doc, tw)]
	private Map<String, Map<Integer, Float>> invertedIndex;


	// temporary data
	// word -> [a list of (doc, tf)]
	private Map<String, Map<Integer, Integer>> invertedTf;
	// doc -> max tf
	private Map<Integer, Integer> maxTfs;

	// stemming result
	private Map<String, Integer> termFreq;
	private Integer maxTf;
	private Map<Integer, Map<String, Integer>> stemmedContent;
	private ArrayList<String> stemmedQuery = new ArrayList<String>();
	private ArrayList<ArrayList<String>> stemmedPhrase;

	// constants
	private ArrayList<String> stopWords;

	// constructor, initialize the stemmer and read a list of stop words
	public Stemmer() throws FileNotFoundException {
		stopWords = new ArrayList<String>();
		Scanner input = new Scanner(new File("data/stopwords.txt"));

		while (input.hasNext()) {
			stopWords.add(input.next());
		}
		input.close();
	}

	public void stemAll(Map<Integer, ArrayList<String>> docContent) {

		// doc -> max tf
		maxTfs = new HashMap<Integer, Integer>();
		// word -> [a list of (doc, tf)]
		invertedTf = new HashMap<String, Map<Integer, Integer>>();

		stemmedContent = new HashMap<Integer, Map<String, Integer>>();

		for (Integer docId : docContent.keySet()) {
			// content for each doc
			ArrayList<String> content = docContent.get(docId);

			if (content != null && content.size() > 0) {
				stem(content);

				if (maxTf > 0 && termFreq != null && termFreq.size() > 0) {
					maxTfs.put(docId, maxTf);
					for (String term : termFreq.keySet()) {
						Integer tf = termFreq.get(term);

						if (invertedTf.containsKey(term)) {
							invertedTf.get(term).put(docId, tf);
						} else {
							Map<Integer, Integer> docToTf = new HashMap<Integer, Integer>();
							docToTf.put(docId, tf);
							invertedTf.put(term, docToTf);
						}
					}

				}
			}
			stemmedContent.put(docId, termFreq);
		}
		if (maxTfs != null && maxTfs.size() > 0 && invertedTf != null
				&& invertedTf.size() > 0) {
			calculateTw(docContent.size());
		} else {
			System.err.println("\nErrors found in stemming document content");
			System.exit(0);
		}
	}

	public void stem(ArrayList<String> content) {
		// result
		termFreq = new HashMap<String, Integer>();
		maxTf = 0;

		if (content != null && content.size() > 0) {

			// temporary data
			Porter porter = new Porter();
			Integer tf = 0;

			for (String term : content) {

				String stemmedTerm = porter.stripAffixes(term); // stem words
				if (!stopWords.contains(stemmedTerm)) { // remove stop words

					// add to resultant list
					if (termFreq.containsKey(stemmedTerm)) {
						tf = termFreq.get(stemmedTerm) + 1;
					} else {
						tf = 1;
					}
					if (tf > maxTf) {
						maxTf = tf;
					}
					termFreq.put(stemmedTerm, tf);
				}
			}
		}
	}
	
	
	public ArrayList<String> stemWithRepeat(String s) {
		Porter porter = new Porter();
		
		List<String> stringList = Arrays.asList(s.split("[^a-zA-Z0-9]"));
		ArrayList<String> stemmedString = new ArrayList<String>();
		if (stringList != null && stringList.size() > 0) {
			ArrayList<String> queryArrayList = new ArrayList<String>(stringList);
			// remove white space
			queryArrayList.removeAll(Arrays.asList("", null));

			for (String term : queryArrayList) {
				String stemmedTerm = porter.stripAffixes(term); // stem words
				
				if (!stopWords.contains(stemmedTerm)) { // remove stop words
					stemmedString.add(stemmedTerm);
				}
			}

		}
		return stemmedString;
	}

	private void calculateTw(int numOfDoc) {
		invertedIndex = new HashMap<String, Map<Integer, Float>>();

		// store : term -> [a list of (doc, tw)]
		for (String term : invertedTf.keySet()) {
			// build a list of (doc, tw)
			Map<Integer, Float> twDocList = new HashMap<Integer, Float>();
			Map<Integer, Integer> tfDocList = invertedTf.get(term);
			Integer df = tfDocList.size();

			for (Integer docId : tfDocList.keySet()) {
				// calculate each doc -> tw
				Integer tf = tfDocList.get(docId);
				Integer maxTf = maxTfs.get(docId);


				Float weight = (float) ((tf * (Math.log((float) numOfDoc / df) / Math

						.log(2))) / maxTf);
				twDocList.put(docId, weight);
			}
			invertedIndex.put(term, twDocList);

		}
	}
	
	public void cleamStemmer(){
		stemmedQuery = new ArrayList<String>();
		stemmedPhrase = new ArrayList<ArrayList<String>>();
	}

	public Map<String, Map<Integer, Float>> getInvertedIndex() {
		return invertedIndex;
	}

	public Map<Integer, Map<String, Integer>> getStemmedContent() {
		return stemmedContent;
	}
	
	public ArrayList<String> getStemmedQuery() {
		return stemmedQuery;
	}
	
	public ArrayList<ArrayList<String>> getStemmedPhrase() {
		return stemmedPhrase;
	}

}
